from typing import Generic, Optional, Sequence, Any, Callable, Type

from fastapi import UploadFile
from fastapi.responses import StreamingResponse

from app.core.cache.base_cache import AsyncBaseCache
from app.core.custom_exception import NotFoundException
from app.core.generic_types import SQLModelType, CreateSchemaType, UpdateSchemaType, ExportSchemaType
from app.db.cache_factory import CacheFactory
from app.db.session_extension import AsyncSessionExtension
from app.models.base import IdType
from app.schemas.query_schema import BaseQueryIn
from app.schemas.result_schema import PageModel
from app.utils.excel_extension import export_excel, export_excel_template, read_excel_to_model_list


class BaseCRUD(Generic[SQLModelType, CreateSchemaType, UpdateSchemaType]):
    """
    初始化CRUD操作类
    :param model: SQLModel 实体类
    :param cache_factory: 用于延迟初始化的缓存工厂方法
    :param session_extension_factory: 用于延迟初始化的会话扩展方法
    """
    def __init__(self, model: type[SQLModelType], session_extension_factory:Callable[[Type[SQLModelType]], AsyncSessionExtension[SQLModelType]], cache_factory: Optional[Callable[[], AsyncBaseCache]]=None):
        self.model = model
        self._session_extension_factory = session_extension_factory
        self._context: Optional[AsyncSessionExtension] = None
        self._cache_factory = cache_factory
        self._cache:Optional[AsyncBaseCache] = None

    @property
    def cache(self) -> AsyncBaseCache:
        """延迟初始化的缓存"""
        if self._cache is None:
            if self._cache_factory is None:
                self._cache = CacheFactory().get_cache_instance()
            self._cache = self._cache_factory()
        return self._cache

    @property
    def context(self) -> AsyncSessionExtension:
        """延迟初始化的会话扩展"""
        if self._context is None:
            self._context = self._session_extension_factory(self.model)
        return self._context


    @property
    def not_deleted_condition(self)->bool:
        """
        获取未删除数据的查询条件
        :return: bool
        """
        return self.model.is_delete == False

    async def validate_by_field(self,field_name:str,field_value:Any) ->Sequence[SQLModelType]:
        db_objs = await self.get_by_field(field_name=field_name,field_value=field_value)
        if not db_objs or len(db_objs) == 0:
            raise NotFoundException(message=f"找不到字段名为 {field_name} 的 {self.model.__name__}")
        return db_objs

    async def validate_by_id(self, id: IdType) -> SQLModelType:
        """
        验证数据是否存在
        :param id: 数据ID
        :return: 存在的数据对象
        :raises HTTPException: 404 如果数据不存在或已被删除
        """
        db_obj = await self.get_by_id(id=id)
        if not db_obj:
            raise NotFoundException(message=f"找不到ID为 {id} 的 {self.model.__name__}")
        return db_obj

    async def validate_by_ids(self, ids: Sequence[IdType]) -> Sequence[SQLModelType]:
        """
        验证多个ID对应的数据是否存在
        :param ids: 数据ID列表
        :return: 存在的数据对象列表
        :raises HTTPException: 404 如果有任何数据不存在或已被删除
        """
        db_objs = await self.get_by_ids(ids=ids)

        # 检查是否有缺失的ID
        found_ids = {obj.id for obj in db_objs}
        missing_ids = set(ids) - found_ids

        if missing_ids:
            raise NotFoundException(message=f"以下ID的数据没有找到: {',\n'.join(str(id) for id in ids)}")
        return db_objs

    async def get_all(self,query_in: BaseQueryIn = None) -> Sequence[SQLModelType]:
        """获取所有没有删除的数据"""
        return await self.context.dynamic_query(query_in).to_list()

    async def get_page(self, current_page: int = 0, page_size: int = 100, query_in: BaseQueryIn = None) -> PageModel[SQLModelType]:
        """
        获取分页数据
        :param current_page: 跳过的记录数
        :param page_size: 每页记录数
        :param query_in: 自定义查询
        :return: 分页数据列表
        """
        return await self.context.dynamic_query(query_in).to_page_list( current_page=current_page,page_size=page_size)

    async def get_by_id(self, id: IdType) -> Optional[SQLModelType]:
        """根据ID获取单个数据"""
        return await self.context.where(self.model.id == id).scalar_one_or_none()

    async def get_by_ids(self, ids: Sequence[IdType]) -> Sequence[SQLModelType]:
        """根据ID列表获取多个数据"""
        return await self.context.where(self.model.id.in_(ids)).to_list()

    async def get_by_field(self,field_name: str,field_value: str,) -> Sequence[SQLModelType]:
        if not hasattr(self.model, field_name):
            raise NotFoundException(message=f"找不到字段名为 {field_name} 的 {self.model.__name__}")
        return await self.context.where(getattr(self.model, field_name) == field_value).to_list()

    async def batch_export(self,model:Type[ExportSchemaType],ids:Sequence[IdType],export_name:str="默认") -> StreamingResponse:
        """批量导出"""
        db_objs = await self.get_by_ids(ids=ids)
        objs = [model(**item.model_dump()) for item in db_objs]
        return await export_excel(data=objs, export_name=export_name)

    async def export_template(self,model:Type[ExportSchemaType],export_name:str="默认") -> StreamingResponse:
        """导出模版"""
        return await export_excel_template(model=model, export_name=export_name)

    async def batch_import(self,model:Type[CreateSchemaType],file:UploadFile) -> Sequence[SQLModelType]:
        """批量导入"""
        objs = await read_excel_to_model_list(model=model, file=file)
        return await self.batch_create(objs=objs)

    async def create(self, obj: CreateSchemaType) -> SQLModelType:
        """
        创建新数据
        :param obj: 创建数据的schema
        :return: 创建的数据对象
        :raises HTTPException: 409 如果数据已存在
        """
        db_obj = self.model.model_validate(obj)
        return await self.context.add(entity=db_obj)

    async def batch_create(self, objs: Sequence[CreateSchemaType]) -> Sequence[SQLModelType]:
        """
        批量创建数据
        :param objs: 创建数据的schema列表
        :return: 创建的数据对象列表
        :raises HTTPException: 409 如果部分或全部数据已存在
        """
        db_objs = [self.model.model_validate(obj_in) for obj_in in objs]
        return await self.context.add_all( entities=db_objs)

    async def update_field(self, id: IdType, field_name: str, field_value: Any) -> Optional[SQLModelType]:
        """
        更新某个字段的值
        :param id: 对象 Id
        :param field_name: 字段名
        :param field_value: 字段值
        :return:
        :raises HTTPException: 404 如果用户不存在或已被删除
        """
        db_obj = await self.validate_by_id(id=id)
        return await self.context.update_field( entity=db_obj, field_name=field_name,field_value=field_value)

    async def update(self, obj: UpdateSchemaType, ) -> SQLModelType:
        """
        更新数据
        :param obj: 更新数据的schema
        :return: 更新后的数据对象
        :raises HTTPException: 404 如果数据不存在
        """
        db_obj = await self.validate_by_id(id=obj.id)
        update_data = obj.model_dump(exclude_unset=True)
        db_obj.sqlmodel_update(update_data)
        return await self.context.update(entity=db_obj)

    async def batch_update(self, objs: Sequence[SQLModelType], ) -> Sequence[SQLModelType]:
        """
        批量更新数据
        :param objs: 更新数据的schema列表
        :return: 更新后的数据对象列表
        :raises HTTPException: 404 如果部分或全部数据不存在
        """
        # 先验证所有ID是否存在
        obj_ids = [obj_new.id for obj_new in objs]
        db_objs = await self.validate_by_ids(ids=obj_ids)

        # 创建ID到对象的映射字典
        obj_map = {obj.id: obj for obj in db_objs}

        # 更新每个对象
        update_objs = []
        for obj_new in objs:
            db_obj = obj_map[obj_new.id]
            update_data = obj_new.model_dump(exclude_unset=True)
            db_obj.sqlmodel_update(update_data)
            update_objs.append(db_obj)

        return await self.context.update_all(entities=update_objs)

    async def delete(self, id: IdType, ) -> SQLModelType:
        """
        软删除数据
        :param id: 要删除的数据ID
        :return: 删除后的数据对象
        :raises HTTPException: 404 如果数据不存在
        """
        db_obj = await self.validate_by_id(id=id)
        return await self.context.delete( entity=db_obj)

    async def batch_delete(self, ids: Sequence[IdType]) -> Sequence[SQLModelType]:
        """
        批量软删除数据
        :param ids: 要删除的数据ID列表
        :return: 删除后的数据对象列表
        :raises HTTPException: 404 如果部分或全部数据不存在
        """
        db_objs = await self.validate_by_ids(ids=ids)
        return await self.context.delete_all(entities=db_objs)

